package com.fengye.common.utils;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SecureDigestAlgorithm;

import javax.crypto.SecretKey;
import java.time.Instant;
import java.util.Date;
import java.util.UUID;

/**
 * @author sunlei
 * @version 1.0
 * @date 2023/9/8 08:55:23
 * @description token工具类 https://github.com/jwtk/jjwt#overview-example-jwt
 */
public class JwtUtil {
    // 过期时间(单位:秒)
    public static Long ACCESS_EXPIRE = 8 * 60 * 60L;
    // 签发者
    private static final String JWT_ISSUE = "com.quban";
    // 证书主题
    private static final String JWT_SUBJECT = "Peripherals";
    // 加密算法
    private static final SecureDigestAlgorithm<SecretKey, SecretKey> JWT_ALGORITHM = Jwts.SIG.HS512;

    /**
     * 私钥
     * 生成签名的时候使用的秘钥secret，一般可以从本地配置文件中读取，切记这个秘钥不能外露，只在服务端使用，在任何场景都不应该流露出去。
     * 一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
     * 加密算法应该大于等于256位(SECRET长度32及以上的字符串)，并且是随机的字符串
     */
    private static final String JWT_SECRET_KEY = "SfsecretKeyasdf.sdf+asfadsfsecretKeySasdf';asfasecret./;[/'/..KeyasdfasfadsfsecretKtKeyasdfdDSFDSFyasdfasfadsf";
    // 秘钥实例
    public static final SecretKey JWT_KEY = Keys.hmacShaKeyFor(JWT_SECRET_KEY.getBytes());


    /**
     * 这些是一组预定义的声明，它们 不是强制性的，而是推荐的 ，以 提供一组有用的、可互操作的声明 。
     * iss: jwt签发者
     * sub: jwt所面向的用户
     * aud: 接收jwt的一方
     * exp: jwt的过期时间，这个过期时间必须要大于签发时间
     * nbf: 定义在什么时间之前，该jwt都是不可用的.
     * iat: jwt的签发时间
     * jti: jwt的唯一身份标识，主要用来作为一次性token,从而回避重放攻击
     * @param userId 用户ID
     * @return token
     */
    public static String genAccessToken(String userId) {
        // 令牌id
        String uuid = UUID.randomUUID().toString();
        Date exprireDate = Date.from(Instant.now().plusSeconds(ACCESS_EXPIRE));

        return Jwts.builder()
                // 设置头部信息header
                .header()
                .add("typ", "JWT")
                .add("alg", "HS512")
                .and()
                // 设置自定义负载信息payload，不在claim里面单独自定义username字段了，外面不好获取，直接用JWT官方指定的subject字段代替即可
                //.claim("username", username)
                // 令牌ID
                .id(uuid)
                // 过期日期
                .expiration(exprireDate)
                // 签发时间
                .issuedAt(new Date())
                // 主题
                //.subject(JWT_SUBJECT)
                .subject(userId)
                // 签发者
                .issuer(JWT_ISSUE)
                // 签名
                .signWith(JWT_KEY, JWT_ALGORITHM)
                .compact();
    }

    /**
     * 解析token
     * @param token token
     * @return Jws<Claims>
     */
    private static Jws<Claims> parseClaim(String token) throws JwtException, IllegalArgumentException {
        Jws<Claims> claims = Jwts.parser()
                .verifyWith(JWT_KEY)
                .build()
                .parseSignedClaims(token);

        return claims;
    }

    /**
     * 获取Token header
     * @param token Token
     * @return header
     */
    public static JwsHeader parseHeader(String token) throws JwtException, IllegalArgumentException {
        return parseClaim(token).getHeader();
    }

    /**
     * 获取Token payload
     * @param token Token
     * @return payload
     */
    public static Claims parsePayload(String token) throws JwtException, IllegalArgumentException {
        return parseClaim(token).getPayload();
    }

    /**
     * 获取userId
     * @param token token
     * @return username
     */
    public static String parseUserId(String token) throws JwtException, IllegalArgumentException {
        Claims claims = parsePayload(token);
        //String username = claims.get("username", String.class);
        String username = claims.getSubject();
        return username;
    }

    public static void main(String[] args) throws Exception {

        String token = genAccessToken("123456");
        System.out.println(token);

        Claims claims = parsePayload(token);
        String username = claims.getSubject();

        System.out.println(claims);
        System.out.println(username);
    }
}
